/*内核模块程序*/

#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/string.h>
#include <asm/uaccess.h>
#include <linux/cdev.h>

#include "led.h"


/*定义结构体类型*/
struct rk3399_led
{
	unsigned int major;
	dev_t dev;
	struct class* pcls;
	struct device* pdev;
	unsigned int gpio_led_stat;
	int value;
	struct cdev* pcdev;
};

/*全局变量*/
static struct rk3399_led* pled;//声明结构体指针，但没分配结构体变量空间


/*文件操作函数*/
static int led_open (struct inode *inode, struct file *filp)
{
	printk("<kernel>call function:%s\n",__FUNCTION__);
	gpio_set_value(pled->gpio_led_stat, 1);//开灯
	
	return 0;
}

static int led_close (struct inode *inode, struct file *filp)
{
	printk("<kernel>call function:%s\n",__FUNCTION__);
	gpio_set_value(pled->gpio_led_stat, 0);//关灯
	
	return 0;
}

static ssize_t led_read(struct file *filp, char __user *buf, size_t size, loff_t *flag)
{
	int ret;
	printk("<kernel>call function:%s\n",__FUNCTION__);

	pled->value = gpio_get_value(pled->gpio_led_stat);
	ret = copy_to_user(buf, &pled->value, sizeof(int));
	if(ret > 0)
	{
		printk("<kernel>copy_to_user fail.\n");
		return -EFAULT;
	}	
	
	return sizeof(int);
}

static ssize_t led_write(struct file *filp, const char __user *buf, size_t size, loff_t *flag)
{
	int ret;
	printk("<kernel>call function:%s\n",__FUNCTION__);

	ret = copy_from_user(&pled->value, buf, size);
	if(ret > 0)
	{
		printk("<kernel>copy_from_user fail.\n");
		return -EFAULT;
	}
	
	if(pled->value == 1)
	{
		printk("<kernel>LED1 ON!\n");
		gpio_set_value(pled->gpio_led_stat, 1);//开灯
	}
	else if(pled->value == 0)
	{
		printk("<kernel>LED1 OFF!\n");
		gpio_set_value(pled->gpio_led_stat, 0);//关灯
	}
	
	return size;
}

static long led_ioctl(struct file * filp, unsigned int cmd, unsigned long arg)
{
	int ret;
	DATA udata;//用户空间发送过来的数据
	static DATA kdata = {'k',0};//要发送给用户空间的数据

	/*解析命令*/
	if(_IOC_TYPE(cmd) != 'L')
	{
		printk("<kernel>NOT LED COMMAND\n");
		return -EFAULT;
	}

	/*执行命令*/
	switch (cmd) 
	{
		case LED_ON:
			if (arg != 1 && arg != 2)
				return -EFAULT;
			else if (arg == 1)
			{
				printk("<kernel>LED1 ON.\n");
				gpio_set_value(pled->gpio_led_stat, 1);//开灯
			}
			else if (arg == 2)
			{
				printk("<kernel>LED2 ON.\n");
				//暂时没有LED2可控制
			}
			break;
		case LED_OFF:
			if (arg != 1 && arg != 2)
				return -EFAULT;
			else if (arg == 1)
			{
				printk("<kernel>LED1 OFF.\n");
				gpio_set_value(pled->gpio_led_stat, 0);//关灯
			}
			else if (arg == 2)
			{
				printk("<kernel>LED2 OFF.\n");
				//暂时没有LED2可控制
			}
			break;
		case LED_ALL_ON:
			printk("<kernel>LED ALL ON.\n");	
			break;
		case LED_ALL_OFF:
			printk("<kernel>LED ALL OFF.\n");	
			break;
		case READ_DATA:
			ret = copy_to_user((DATA __user *)arg, &kdata, sizeof(DATA));
			if(ret > 0)
			{
				printk("<kernel>copy_to_user fail.\n");
				return -EFAULT;
			}
			kdata.data2++;
			break;
		case WRITE_DATA:
			ret = copy_from_user(&udata, (DATA __user *)arg, sizeof(DATA));
			if(ret > 0)
			{
				printk("<kernel>copy_from_user fail.\n");
				return -EFAULT;
			}
			printk("<kernel>recv data:%c %d\n",kdata.data1,kdata.data2);
			break;
			
		default:
			printk("<kernel>UNKNOWN COMMAND.\n");
			break;
	}

	return 0;
}



/*改变系统调用接口地址*/
static struct file_operations fops =
{
	.owner = THIS_MODULE,
	.open = led_open,
	.release = led_close,
	.read = led_read,
	.write = led_write,
	.unlocked_ioctl = led_ioctl,
};


/*入口函数*/
static int __init led_init(void)
{
	int ret = 0;
	printk("<kernel>call function:%s\n",__FUNCTION__);

	/*申请分配结构体空间*/
	pled = kmalloc(sizeof(struct rk3399_led), GFP_KERNEL);
	if(pled == NULL)
	{
		printk("kmalloc fail\n");
		return -ENOMEM;
	}

	/*注册主设备号（新版、动态）*/
	ret = alloc_chrdev_region(&pled->dev, 123, 1, "led_drv");
	if(ret < 0)
	{
		printk("<kernel>register(new) major fail.\n");
		ret = -EBUSY;
		goto err_alloc_chrdev_region;
	}	
	pled->major = MAJOR(pled->dev);

	printk("<kernel>register(new) major success!\n");
	printk("<kernel>major = %d\n",pled->major);

	/*创建、初始化、注册字符设备驱动*/
	pled->pcdev = cdev_alloc();
	cdev_init(pled->pcdev, &fops);
	cdev_add(pled->pcdev, pled->dev, 1);

	/*创建设备类*/
	pled->pcls = class_create(THIS_MODULE, "led_cls");
	if (IS_ERR(pled->pcls))
	{
		ret = PTR_ERR(pled->pcls);
		goto err_class_create;
	}

	/*创建设备文件*/
	pled->pdev = device_create(pled->pcls,NULL, MKDEV(pled->major, 123),NULL,"led");
	if (IS_ERR(pled->pdev))
	{
		ret = PTR_ERR(pled->pdev);
		goto err_device_create;
	}	

	/*初始化硬件*/
	pled->gpio_led_stat = 13;
	ret = gpio_request(pled->gpio_led_stat, "led_stat");//申请使用权
	if(ret != 0)
	{
		printk("gpio_request fail\n");
		goto err_gpio_request;
	}
	gpio_direction_output(pled->gpio_led_stat, 0);//初始状态：关灯
	gpio_free(pled->gpio_led_stat);//释放使用权

	return 0;

	/*集中容错处理*/
err_gpio_request:
	device_destroy(pled->pcls, MKDEV(pled->major, 123));
err_device_create:
	class_destroy(pled->pcls);
err_class_create:
	cdev_del(pled->pcdev);
	unregister_chrdev_region(pled->dev, 1);
err_alloc_chrdev_region:
	kfree(pled);	
	
	return ret;
}

/*出口函数*/
static void __exit led_exit(void)
{
	printk("<kernel>call function:%s\n",__FUNCTION__);

	/*销毁设备文件*/
	device_destroy(pled->pcls, MKDEV(pled->major, 123));

	/*销毁设备类*/
	class_destroy(pled->pcls);

	/*注销字符设备驱动*/
	cdev_del(pled->pcdev);
	
	/*注销主设备号*/
	unregister_chrdev(pled->major, "led_drv");
	printk("<kernel>unregister major success!\n");

	/*释放堆区空间*/
	kfree(pled);
}


module_init(led_init); //告诉内核本模块程序的入口函数名
module_exit(led_exit);//告诉内核本模块程序的出口函数名
MODULE_LICENSE("GPL"); //遵守开源许可协议



